package top.guoziyang.mydb.backend.dm.dataItem;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import top.guoziyang.mydb.backend.common.SubArray;
import top.guoziyang.mydb.backend.dm.DataManagerImpl;
import top.guoziyang.mydb.backend.dm.page.Page;

/**
 * dataItem 结构如下：
 * [ValidFlag] [DataSize] [Data]
 * ValidFlag 1字节，0为合法，1为非法
 * DataSize  2字节，标识Data的长度
 */
public class DataItemImpl implements DataItem {

    static final int OF_VALID = 0;//ValidFlag 起始位置
    static final int OF_SIZE = 1;//DataSize 起始位置
    static final int OF_DATA = 3;// Data 起始位置

    private SubArray raw;// 存储数据的字节数组（包含头部信息）
    private byte[] oldRaw;// 旧数据的备份（用于回滚）
    private Lock rLock;// 读锁
    private Lock wLock;// 写锁
    private DataManagerImpl dm;// 数据管理器（用于日志记录等）
    private long uid; // 数据项唯一标识
    private Page pg;// 该数据项所在的页面

    public DataItemImpl(SubArray raw, byte[] oldRaw, Page pg, long uid, DataManagerImpl dm) {
        this.raw = raw; //存储当前数据项的 原始数据（包含头部信息）。
        this.oldRaw = oldRaw;//旧数据备份（用于回滚）。
        /**
         * 创建了一个 可重入的读写锁
         *  可重入 的，这意味着同一个线程可以多次获得同一把锁而不发生死锁。
         *  使用场景:
         * 在一个应用程序中，读操作通常比写操作频繁发生。
         * 例如，在数据库应用中，大多数操作都是查询，而写操作较少。
         * 使用 ReentrantReadWriteLock 可以允许多个线程同时读取数据，提高性能，
         * 而写操作依然能保证互斥。
         */
        ReadWriteLock lock = new ReentrantReadWriteLock();
        rLock = lock.readLock();
        wLock = lock.writeLock();
        this.dm = dm;//数据所在的 数据库页面。
        this.uid = uid;//数据项的 唯一标识。
        this.pg = pg;//数据管理器，用于 日志管理 和 数据项释放。
    }

    /**
     * 判断DataItemImpl是否合法
     * raw是构造方法创建DataItemImpl对象时的SubArray类型参数，它有3个参数：raw，start，end
     */
    public boolean isValid() {
        return raw.raw[raw.start+OF_VALID] == (byte)0;
    }

    /**
     * 获取DataItemImpl数据项的有效数据部分，封装成SubArray格式
     */
    @Override
    public SubArray data() {
        return new SubArray(raw.raw, raw.start+OF_DATA, raw.end);
    }

    /**
     * 在修改 DataItemImpl 数据项之前，将数据项的当前状态备份，以便在需要时能够恢复。
     */
    @Override
    public void before() {
        wLock.lock();
        pg.setDirty(true);
        /**
         * System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
         * 该方法用于在两个数组之间高效地复制元素：
         * src：源数组
         * srcPos：源数组的起始位置
         * dest：目标数组
         * destPos：目标数组的起始位置
         * length：要复制的长度
         */
        //将 raw.raw 数组中从 raw.start 开始的 oldRaw.length 个字节复制到 oldRaw 数组中。
        System.arraycopy(raw.raw, raw.start, oldRaw, 0, oldRaw.length);
    }


    /**
     * 撤销修改（回滚）
     */
    @Override
    public void unBefore() {
        // 调用 unBefore() 方法,用备份数据项覆盖当前数据项
        System.arraycopy(oldRaw, 0, raw.raw, raw.start, oldRaw.length);
        // 释放之前在before()方法中加的写锁（wLock），允许其他线程对该数据项进行写操作
        wLock.unlock();
    }


    /**
     * 数据项修改后，生成对应的日志
     */
    @Override
    public void after(long xid) {
        // 为xid生成一条update日志,并记录当前数据项( this) 的内容
        dm.logDataItem(xid, this);
        // 释放之前在before()方法中加的写锁（wLock），允许其他线程对该数据项进行写操作
        wLock.unlock();
    }

    /**
     * 释放当前数据项DataItemImpl的缓存资源
     */
    @Override
    public void release() {
        dm.releaseDataItem(this);
    }
    /**
     * 写锁
     */
    @Override
    public void lock() {
        wLock.lock();
    }

    @Override
    public void unlock() {
        wLock.unlock();
    }

    /**
     * 读锁
     */
    @Override
    public void rLock() {
        rLock.lock();
    }

    @Override
    public void rUnLock() {
        rLock.unlock();
    }

    /**
     * 所在页面
     */
    @Override
    public Page page() {
        return pg;
    }

    /**
     * 唯一ID
     */
    @Override
    public long getUid() {
        return uid;
    }

    /**
     * 旧数据
     */
    @Override
    public byte[] getOldRaw() {
        return oldRaw;
    }

    /**
     * 原始数据
     */
    @Override
    public SubArray getRaw() {
        return raw;
    }
    
}
